Skip to content

refactor(sidecars): move boot and the watchers out of index.ts - #2215

Merged
MODSetter merged 2 commits into
MODSetter:devfrom
llwand1:refactor/docker-03-sidecar-boot
Oct 11, 2026
Merged

MODSetter merged 2 commits into
MODSetter:devfrom
llwand1:refactor/docker-03-sidecar-boot

Conversation

@llwand1

@llwand1 llwand1 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

What

  • bootSidecars() moves from index.ts to sidecars/boot.ts. It takes its paths, the packaged flag and the secret as a BootContext, instead of reading app, process.resourcesPath and safeStorage.
  • Each watcher moves to its own file: watch-generation-preset.ts, watch-audio-models.ts, watch-image-model.ts, with tests of their own. The opencode watcher stays in boot.ts, since only boot owns that lifecycle.
  • index.ts builds the context from Electron and passes its crash notice to the window in as the crash handler.
  • The watchers take a SidecarControl (the running children and how to change them), so a test hands it fakes and spawns nothing.
  • bootSidecars() now returns before waiting on /health: the caller holds the sidecars and the watchers' stop before anything can fail, so a failed startup still reaps them.

Why

Step 3 of 14 of #2100, so the container can start the same sidecars without Electron. No file under sidecars/ imports electron now.

Fixes #2103

How to test

  • cd surfsense_local/electron && pnpm typecheck
  • cd surfsense_local/electron && pnpm test
  • pnpm dev boots as before

High-level PR Summary

This PR refactors the sidecar boot process by moving bootSidecars() from index.ts into a new sidecars/boot.ts file and extracting each model watcher (generation preset, audio models, image models) into separate testable modules. The refactoring introduces a BootContext interface that decouples the boot logic from Electron-specific APIs, allowing the same code to run in a containerized environment. Each watcher now receives a SidecarControl interface for managing child processes, enabling unit tests with fake implementations that don't spawn actual processes. The boot function now returns immediately with handles to the sidecars and watchers, deferring health checks to the caller to ensure proper cleanup even if startup fails.

⏱️ Estimated Review Time: 30-90 minutes

💡 Review Order Suggestion
Order File Path
1 surfsense_local/electron/src/main/sidecars/file-stamp.ts
2 surfsense_local/electron/src/main/sidecars/supervisor.ts
3 surfsense_local/electron/src/main/sidecars/watch-image-model.ts
4 surfsense_local/electron/src/main/sidecars/watch-generation-preset.ts
5 surfsense_local/electron/src/main/sidecars/watch-audio-models.ts
6 surfsense_local/electron/src/main/sidecars/watch-image-model.test.ts
7 surfsense_local/electron/src/main/sidecars/watch-generation-preset.test.ts
8 surfsense_local/electron/src/main/sidecars/watch-audio-models.test.ts
9 surfsense_local/electron/src/main/sidecars/boot.ts
10 surfsense_local/electron/src/main/index.ts

Need help? Join our Discord

Summary by CodeRabbit

  • New Features
    • Local image, audio, and language-model services now respond to changes in selected models or runtime settings without requiring an app restart.
    • The OpenCode service starts when its configuration is available and stops when that configuration is removed.
    • The app checks that its local API is healthy during startup and stops background monitoring cleanly when shutting down.

bootSidecars() moves to sidecars/boot.ts and takes its paths, the packaged flag and the secret as a BootContext, instead of reading app, process.resourcesPath and safeStorage. The three watchers get their own files and tests. index.ts builds the context from Electron and passes the crash notice to the window in as the crash handler.

Nothing under sidecars/ imports electron, so the container can run the same code.

Fixes MODSetter#2103
@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: MODSetter/SurfSense/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f558bdb6-46f6-4eee-9948-103307b34e12

📥 Commits

Reviewing files that changed from the base of the PR and between d992b72 and d730e64.


📒 Files selected for processing (8)
  • surfsense_local/electron/src/main/index.ts
  • surfsense_local/electron/src/main/sidecars/boot.ts
  • surfsense_local/electron/src/main/sidecars/supervisor.ts
  • surfsense_local/electron/src/main/sidecars/watch-audio-models.test.ts
  • surfsense_local/electron/src/main/sidecars/watch-audio-models.ts
  • surfsense_local/electron/src/main/sidecars/watch-generation-preset.test.ts
  • surfsense_local/electron/src/main/sidecars/watch-generation-preset.ts
  • surfsense_local/electron/src/main/sidecars/watch-image-model.ts

🚧 Files skipped from review as they are similar to previous changes (8)
  • surfsense_local/electron/src/main/sidecars/watch-generation-preset.ts
  • surfsense_local/electron/src/main/sidecars/watch-audio-models.test.ts
  • surfsense_local/electron/src/main/sidecars/watch-generation-preset.test.ts
  • surfsense_local/electron/src/main/sidecars/watch-image-model.ts
  • surfsense_local/electron/src/main/sidecars/watch-audio-models.ts
  • surfsense_local/electron/src/main/sidecars/boot.ts
  • surfsense_local/electron/src/main/sidecars/supervisor.ts
  • surfsense_local/electron/src/main/index.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.



📝 Walkthrough

Walkthrough

Sidecar startup and watcher management move into sidecar modules. Electron main builds the boot context and uses the returned API URL, sidecars, and health check. Watchers stop during shutdown, and tests cover audio, generation-preset, and image-model watcher behavior.

Changes

Sidecar boot and watcher management

Layer / File(s) Summary
Sidecar controls and configuration watchers
surfsense_local/electron/src/main/sidecars/supervisor.ts, file-stamp.ts, watch-audio-models.ts, watch-audio-models.test.ts, watch-generation-preset.ts, watch-generation-preset.test.ts, watch-image-model.ts, watch-image-model.test.ts
Adds shared sidecar controls, watcher options, file-stamp lookup, and polling watchers for audio, generation-preset, and image-model sidecars. Tests cover configuration changes, unchanged or unavailable inputs, and shutdown behavior.
Boot context and sidecar orchestration
surfsense_local/electron/src/main/sidecars/boot.ts
Adds bootSidecars to build runtime context, start sidecars, install watchers, and return the API URL, health check, and watcher cleanup. Adds OpenCode agent-config reconciliation.
Electron boot and shutdown integration
surfsense_local/electron/src/main/index.ts
Electron main builds the boot context and calls bootSidecars. Startup uses the returned API URL, context, and health check. Shutdown stops watchers.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Refactor


Merge Risk: ⚪ Minimal · up to d730e

This refactor moves sidecar startup and watcher code into separate modules without intended behavior changes. No concrete merge-blocking risk was found in the supplied context.

Security Architecture Review

Security architecture risk: 🔵 Low · up to d992b

The refactor preserves the current application's path, secret, and local-network authority. No new externally reachable entrypoint or security regression was identified. Risk remains low because process ownership and cleanup now cross an explicit startup boundary, while exceptional startup and concurrent shutdown behavior were assessed from source rather than exercised.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The reviewed authority is scoped to one local application installation and its supervised backend, model runtimes, agent, and data directory. Extraction does not establish greater remote, tenant, or environment exposure; security properties of a future host adapter remain outside this review.

Trust Boundaries and Controls

  • inferred — Accepting paths and secrets through BootContext does not itself create an attacker-controlled entrypoint. The current caller constructs these values from Electron host APIs, and orchestration retains local addressing and existing executable specifications. These controls bound the extraction's exposure but do not establish complete backend or agent security.

Resilience and Maintainability Implications

  • observed — Existing reconciliation can remain in flight after shutdown begins, and interval callbacks are not serialized. Those limitations predate this PR; explicit timer disposal does not amount to cancellation or draining of already-running work. They are not retained as introduced architecture concerns.

Hardening Proposals

  • proposed — As separate lifecycle hardening, serialize reconciliation and drain or cancel in-flight work before terminating children. This would strengthen process-containment guarantees around the pre-existing shutdown race rather than repair a demonstrated regression from this extraction.



Pre-merge checks | Passed 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: moving sidecar boot logic and watchers from index.ts into dedicated sidecar modules.
Linked Issues check Passed Issue #2103 requires reusable sidecar boot code, separate watcher modules, no Electron imports under src/main/sidecars/, and watcher tests. The PR adds sidecars/boot.ts with BootContext, moves t…
Out of Scope Changes check Passed The reviewed changes are limited to sidecar source files, index.ts, and related tests. supervisor.ts and file-stamp.ts support the watcher extraction and sidecar control design. The changes do n…
Docstring Coverage Passed Docstring coverage is 90.32% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 31 functions across 10 files.

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR

🧪 Generate unit tests (beta)
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@vercel

vercel Bot commented Oct 10, 2026

Copy link
Copy Markdown

@llwand1 is attempting to deploy a commit to the Rohan Verma's projects Team on Vercel.

A member of the Team first needs to authorize it.

@llwand1

llwand1 commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

Evidence for the linked checks (follow-up commit d730e64):

1. Docstring coverage — added JSDoc to the functions the diff touches (bootSidecars, watchAgentConfig, the internal reconcile functions, spawnOne, startAll, stopOne, stopAll, plus the settle test helpers and main/shutdown in index.ts). Comments only, no logic changes.

2. pnpm typecheck

$ tsc --noEmit -p tsconfig.json
exit 0

3. Tests — the watcher suites added by this step:

$ node --test src/main/sidecars/watch-audio-models.test.ts \
             src/main/sidecars/watch-generation-preset.test.ts \
             src/main/sidecars/watch-image-model.test.ts
# tests 13
# pass 13
# fail 0

And supervisor.test.ts: # pass 8, # skipped 1, # fail 0. The full suite is green in CI (Desktop Tests / Electron check above).

4. No Electron coupling — nothing under sidecars/ imports the electron module:

$ Select-String -Path "src/main/sidecars/*.ts" -Pattern "from ['""]electron['""]|require\(['""]electron['""]\)"
Import hits: 0

(The word "Electron" appears only inside comments.)

Ready for review — happy to address any feedback.

@MODSetter
MODSetter merged commit b2b99c8 into MODSetter:dev Oct 11, 2026
22 of 23 checks passed
@MODSetter

Copy link
Copy Markdown
Owner

Merged into dev, thanks, and #2103 is closed. Line by line, the moved boot and watchers do what they did before, and returning before /health keeps a failed start reaped now that boot.ts can't set index.ts's variables. Two things for your next step, neither blocking:

  1. apiUrl is now set before the API answers. index.ts:481 assigns apiUrl = boot.apiUrl before await boot.waitHealthy() at :485, while the comment at :77 still says "once it answers". On macOS, Cmd+Q during startup now sends GET /chat/runs to an API that isn't up, and waits up to 2 s for it, where it used to quit at once. Moving that one line below the health wait restores it.
  2. waitHealthy only fails fast if the API dies after it's called. boot.ts:178 passes sidecars.get("api") to waitForHealth, which listens with child.once("exit"), so an exit that has already happened is missed. Electron calls it at once, so nothing breaks today, but step 5's headless entry would sit out the full 60 s and report "not healthy" instead of "sidecar exited during startup". Check exitCode/signalCode first.

Small, for when you're there: no test checks the stop function each watcher returns (a return () => {} passes all 13), and the new files have no trailing newline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants